<?php

/**
 * mForge Framework
 *
 * LICENSE:
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * The license is bundled with this package in the file LICENSE.txt.
 * You may also obtain a copy of the License at this URL:
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @copyright  Copyright (c) 2011, Jonathan Hedrén
 * @author     Jonathan Hedrén <jonathan.hedren@gmail.com>
 * @license    http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
 */

namespace mForge\Helpers;

class HtmlBinder
{
    /**
     *
     * @var string
     */
    private static $idPrefix;

    /**
     *
     * @var array
     */
    private static $propertyMetaData = array();

    /**
     *
     * @var ValidationResult
     */
    private static $validationResult;

    /**
     *
     * @param string $idPrefix
     */
    public static function setIdPrefix($idPrefix)
    {
        self::$idPrefix = $idPrefix;
    }

    /**
     *
     * @param ValidationResult $validationResult
     */
    public static function setValidationResult(\mForge\ValidationResult $validationResult)
    {
        self::$validationResult = $validationResult;
    }

    /**
     *
     * @param object $object
     * @param string $propertyName
     * @param array|string $htmlAttributes
     */
    public static function checkBoxFor($object, $propertyName, $htmlAttributes = null)
    {
        $propertyMetaData = self::getPropertyMetaDataFor($object, $propertyName);
        $id = self::getIdFor($propertyMetaData);

        $checked = '';
        if ((bool) $object->$propertyName) {
            $checked = ' checked="checked"';
        }
        echo '<input type="hidden" name="', $propertyMetaData->name, '" value="0" />', "\n", '<input type="checkbox" name="', $propertyMetaData->name, '" id="', $id, '" value="1"', $checked, ' ', $htmlAttributes, "/>\n";
    }

    /**
     *
     * @param object $object
     * @param string $propertyName
     */
    public static function descriptionFor($object, $propertyName)
    {
        $propertyMetaData = self::getPropertyMetaDataFor($object, $propertyName);
        echo $propertyMetaData->description;
    }

    /**
     *
     * @param object $object
     * @param string $propertyName
     */
    public static function displayNameFor($object, $propertyName)
    {
        $propertyMetaData = self::getPropertyMetaDataFor($object, $propertyName);
        echo $propertyMetaData->displayName;
    }

    /**
     *
     * @param object $object
     * @param string $propertyName
     * @param array|string $htmlAttributes
     */
    public static function hiddenFor($object, $propertyName, $htmlAttributes = null)
    {
        self::inputFor("hidden", $object, $propertyName, $htmlAttributes);
    }

    /**
     *
     * @param object $object
     * @param string $propertyName
     * @param string $text
     * @param array|string $htmlAttributes
     */
    public static function labelFor($object, $propertyName, $text = null, $htmlAttributes = null)
    {
        $pBC = self::getPropertyMetaDataFor($object, $propertyName);
        if ($text === null) {
            $displayName = $pBC->displayName;

            $text = ($displayName == null) ? ucfirst($propertyName) : $displayName;
        }

        $id = self::getIdFor($pBC);
        echo '<label for="', $id, '"', $htmlAttributes, '>', $text, "</label>\n";
    }

    /**
     *
     * @param object $object
     * @param string $propertyName
     * @param string $value
     * @param array|string $htmlAttributes
     */
    public static function radioButtonFor($object, $propertyName, $value, $htmlAttributes = null)
    {
        $propertyMetaData = self::getPropertyMetaDataFor($object, $propertyName);

        // TODO: Append escaped $value to $id
        $id = self::getIdFor($propertyMetaData);
        $checked = '';
        if ($object->$propertyName == $value) {
            $checked = ' checked="checked"';
        }
        echo '<input type="radio" name="', $propertyMetaData->name, '" value="' , $value, '" id="', $id, '"', $checked, ' ', $htmlAttributes, "/>\n";
    }


    /**
     *
     * @param object $object
     * @param string $propertyName
     * @param string $value
     * @param array|string $htmlAttributes
     */
    public static function selectFor($object, $propertyName, array $values, $htmlAttributes = null)
    {
        $propertyMetaData = self::getPropertyMetaDataFor($object, $propertyName);

        $id = self::getIdFor($propertyMetaData);

        $select = '<select name="' . $propertyMetaData->name . '" id="' . $id . '"' . $htmlAttributes . '>';

        $isAssociative = \mForge\Utility::isArrayAssociative($values);

        foreach ($values as $value => $label) {
            if ($isAssociative) {
                $select .= "\n<option value=\"" . $value . '"';
                if ($object->$propertyName == $value) {
                    $select .= ' selected="selected"';
                }
            } else {
                $select .= "\n<option";
                if ($object->$propertyName == $label) {
                    $select .= ' selected="selected"';
                }
            }
            
            $select .= '>' . $label . '</option>';
        }
        $select .= "\n</select>";
        echo $select;
    }

    /**
     *
     * @param object $object
     * @param string $propertyName
     * @param array|string $htmlAttributes
     */
    public static function textBoxFor($object, $propertyName, $htmlAttributes = null)
    {
        self::inputFor("text", $object, $propertyName, $htmlAttributes);
    }
    
    /**
     *
     * @param string $type
     * @param object $object
     * @param string $propertyName
     * @param array|string $htmlAttributes
     */
    public static function inputFor($type, $object, $propertyName, $htmlAttributes = null)
    {
        $propertyMetaData = self::getPropertyMetaDataFor($object, $propertyName);
        $id = self::getIdFor($propertyMetaData);
        echo '<input type="', $type, '" name="', $propertyMetaData->name, '" id="', $id, '" value="', $object->$propertyName, '" ', $htmlAttributes, "/>\n";
    }

    /**
     *
     * @param object $object
     * @param string $propertyName
     * @param array|string $htmlAttributes
     */
    public static function passwordFor($object, $propertyName, $htmlAttributes = null)
    {
        $propertyMetaData = self::getPropertyMetaDataFor($object, $propertyName);
        $id = self::getIdFor($propertyMetaData);
        echo '<input type="password" name="', $propertyMetaData->name, '" id="', $id, '" value="" ', $htmlAttributes, "/>\n";
    }
    
    /**
     *
     * @param object $object
     * @param string $propertyName
     * @return PropertyMetaData
     */
    private static function getPropertyMetaDataFor($object, $propertyName)
    {
        if ($object === null || $propertyName === null)
        {
            throw new InvalidArgumentException('The arguments must not be null.');
        }
        $objectReflection = new \ReflectionObject($object);
        $name = $objectReflection->getShortName() . '[' . $propertyName . ']';

        if (!isset(self::$propertyMetaData[$name])) {

            $attributes = \mForge\AttributeParser::getPropertyAttributesFor($object, $propertyName);
            $propertyMetaData = (object)array(
                'name' => $name,
                'description' => null,
                'displayName' => null
            );
            if ($attributes != null) {
                foreach ($attributes as $attribute) {
                    if ($attribute instanceof \mForge\DescriptionAttribute) {
                        $propertyMetaData->description = $attribute->getDescription();
                    } else if ($attribute instanceof \mForge\DisplayNameAttribute) {
                        $propertyMetaData->displayName = $attribute->getDisplayName();
                    }
                }
            }
            self::$propertyMetaData[$name] = $propertyMetaData;
        }
        return self::$propertyMetaData[$name];
    }

    /**
     *
     * @param StdClass $propertyMetaData
     * @return string
     */
    private static function getIdFor($propertyMetaData)
    {
        $id = str_replace(']', '', str_replace('[', '-', $propertyMetaData->name));
        if (self::$idPrefix == null) {
            return $id;
        }
        return self::$idPrefix . '-' . $id;
    }

    /**
     *
     * @param object $object
     * @param string $propertyName
     * @param array|string $htmlAttributes
     */
    public static function textareaFor($object, $propertyName, $htmlAttributes = null)
    {
        $propertyMetaData = self::getPropertyMetaDataFor($object, $propertyName);
        $id = self::getIdFor($propertyMetaData);
        echo '<textarea name="', $propertyMetaData->name, '" id="', $id, '" ', $htmlAttributes, ">", $object->$propertyName, "</textarea>";
    }

    /**
     *
     * @param object $object
     * @param string $propertyName
     */
    public static function validationMessagesFor($object, $propertyName)
    {
        if (self::$validationResult == null
            || !count(self::$validationResult->getValidationMessages($propertyName))) {
                return;
        }
        if (!self::$validationResult->isFieldValid($propertyName)) {
            $propertyMetaData = self::getPropertyMetaDataFor($object, $propertyName);
            $displayName = $propertyMetaData->displayName;
            $fieldName = ($displayName == null) ? ucfirst($propertyName) : $displayName;
            foreach (self::$validationResult->getValidationMessages($propertyName) as $validationMessage) {
                echo '<span>', sprintf(\mForge\Translator::translate($validationMessage), $fieldName), '</span>';
            }
        }
    }
}